package jj.fly.activiti.leavebill.service.impl;

import jj.fly.activiti.leavebill.service.IEmployeeService;
import jj.fly.activiti.leavebill.vo.SubmitTaskVo;
import jj.fly.activiti.leavebill.domain.Employee;
import jj.fly.activiti.leavebill.domain.LeaveBill;
import jj.fly.activiti.leavebill.service.ILeaveBillService;
import jj.fly.activiti.leavebill.service.IWorkflowService;
import org.activiti.engine.*;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.impl.pvm.PvmTransition;
import org.activiti.engine.impl.pvm.process.ActivityImpl;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpSession;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * Author: jiangjun
 * Date: 2018/5/9
 * Time: 上午9:48
 * Description:
 */
@Service("workflowService")
public class WorkflowServiceImpl implements IWorkflowService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private FormService formService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private ILeaveBillService leaveBillService;

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private HttpSession session;


    @Override
    public void deploy(String name, byte[] file) throws Exception {
        try {
            ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(file));
            repositoryService.createDeployment()
                    .name(name)
                    .addZipInputStream(zipInputStream)
                    .deploy();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public List<Deployment> getDeploymentList() {
        List<Deployment> list = repositoryService.createDeploymentQuery()
                .orderByDeploymenTime().desc()
                .list();
        return list;
    }

    @Override
    public List<ProcessDefinition> getProcessDefinitionList() {
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
                .orderByProcessDefinitionName().desc()
                .list();
        return list;
    }

    @Override
    public InputStream getImageInputStream(String deployId, String imageName) {
        return repositoryService.getResourceAsStream(deployId, imageName);
    }

    @Override
    public void deleteProcessDefinitionByDeployId(String deployId) {
        repositoryService.deleteDeployment(deployId, true);
    }

    @Override
    public void startProcess(Integer leaveBillId) {
        LeaveBill leaveBill = leaveBillService.getDataById(leaveBillId);
        // 更新状态
        leaveBill.setState(1);
        leaveBillService.save(leaveBill);
        // 使用当前对象获取到流程定义的key
        String key = leaveBill.getClass().getSimpleName();

        // 启动流程实例
        /**
         * 从session中获取当前任务的办理人，使用流程变量设置下一个任务办理人
         */
        Employee currentUser = (Employee) session.getAttribute("current_user");
        Map<String, Object> variables = new HashMap<>();
        variables.put("applyUser", currentUser.getName());

        String objId = key + "." + leaveBillId; // BUSINESS_KEY
        variables.put("objId", objId);
        runtimeService.startProcessInstanceByKey(key, objId, variables);
    }

    @Override
    public List<Task> getTaskListByEmployeeName(String name) {
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee(name)
                .orderByTaskCreateTime().desc()
                .list();
        return list;
    }

    @Override
    public String getTaskFormKeyByTaskId(String taskId) {
        TaskFormData taskFormData = formService.getTaskFormData(taskId);
        return taskFormData.getFormKey();
    }

    @Override
    public LeaveBill getLeaveBillByTaskId(String taskId) {
        // 使用taskId获取任务对象
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        // 获取流程实例id
        String processInstanceId = task.getProcessInstanceId();
        // 根据流程实例idc查询正在执行的执行对象表，获取流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        // 根据流程实例获取BUSSINESS_KEY
        String businessKey = processInstance.getBusinessKey();
        String id = "";
        if(StringUtils.isNoneBlank(businessKey)){
            // 获取请假单id
            id = businessKey.split("\\.")[1];
        }
        LeaveBill leaveBill = leaveBillService.getDataById(Integer.valueOf(id));
        return leaveBill;
    }

    @Override
    public List<String> getOutcomeListByTaskId(String taskId) {
        // 根据taskId查询task对象
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        // 获取流程定义id
        String processDefinitionId = task.getProcessDefinitionId();
        // 查询ProcessDefinitionEntity
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);

        // 获取流程实例id
        String processInstanceId = task.getProcessInstanceId();
        // 根据流程实例idc查询正在执行的执行对象表，获取流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        String activityId = processInstance.getActivityId();
        // 获取当前的活动
        ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);

        List<String> listName = new ArrayList<>();
        // 获取当前活动完成之后连线的名称
        List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
        if(pvmList != null && pvmList.size() > 0){
            for(PvmTransition pvmTransition : pvmList){
                String name = (String) pvmTransition.getProperty("name");
                if(StringUtils.isNoneBlank(name)){
                    listName.add(name);
                }else{
                    listName.add("提交");
                }
            }
        }
        return listName;
    }

    @Override
    public void submitTask(SubmitTaskVo vo) {
        // 添加批注信息
        Task task = taskService.createTaskQuery()
                .taskId(vo.getTaskId())
                .singleResult();
        /**
         * 添加批注的时候，底层实现如下：
         *  String userId = Authentication.getAuthenticatedUserId();
         *     CommentEntity comment = new CommentEntity();
         *     comment.setUserId(userId);
         *
         *  所以需要设置审核人，对应act_hi_comment表中的USER_ID字段
         */
        Employee currentUser = (Employee) session.getAttribute("current_user");
        Authentication.setAuthenticatedUserId(currentUser.getName());

        taskService.addComment(task.getId(), task.getProcessInstanceId(), vo.getComment());

        // 完成任务，并设置流程变量
        Map<String, Object> variables = new HashMap<>();
        if(StringUtils.isNoneBlank(vo.getOutcome()) && !vo.getOutcome().equals("提交")){
            // 设置流程变量
            variables.put("outcome", vo.getOutcome());
        }
        Employee manager = employeeService.getDataById(currentUser.getManagerId());
        if(manager != null) {
            variables.put("approveUserId", manager.getName());
        }

        taskService.complete(vo.getTaskId(), variables);

        // 判断流程是否结束，并更新请假单状态
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId())
                .singleResult();
        if(processInstance == null){
            // 流程结束，则更新请假单状态
            LeaveBill leaveBill = leaveBillService.getDataById(vo.getId());
            leaveBill.setState(2);
            leaveBillService.save(leaveBill);
        }
    }

    @Override
    public List<Comment> getCommentByTaskId(String taskId) {
        List<Comment> listComments = new ArrayList<>();
        // 使用当前taskId，查询当前流程对应的历史任务id
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        String processInstanceId = task.getProcessInstanceId();
        // 使用流程实例id查询历史任务对应的每个任务id
//        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
//                .processInstanceId(processInstanceId)
//                .list();
//        for(HistoricTaskInstance instance : list){
//            String htaskId = instance.getId();
//            List<Comment> taskComments = taskService.getTaskComments(htaskId);
//            listComments.addAll(taskComments);
//        }
        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);

        return processInstanceComments;
    }

    @Override
    public List<Comment> getCommentByLeaveBillId(Integer id) {
        LeaveBill leaveBill = leaveBillService.getDataById(id);
        // 组织BUSSINESS_KEY
        String key = leaveBill.getClass().getSimpleName() + "." + id;
        // 获取流程实例方式一：使用历史流程实例查询，返回历史流程实例对象，从而获取流程实例id
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceBusinessKey(key)
                .singleResult();

        // 获取流程实例方式二：使用历史流程变量查询，返回历史的流程变量对象，获取流程实例id
        String variableName = "objId";
        String variableValue = key;
        HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery()
                .variableValueEquals(variableName, variableValue)  // 实例流程变量的名称和流程变量的值
                .singleResult();

        String processInstanceId = historicVariableInstance.getProcessInstanceId();

        //String processInstanceId = processInstance.getId();

        // 使用历史流程变量查询
        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(processInstanceId);

        return processInstanceComments;
    }

    @Override
    public ProcessDefinition getProcessDefinitionByTaskId(String taskId) {
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        String processDefinitionId = task.getProcessDefinitionId();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();
        return processDefinition;
    }

    @Override
    public Map<String, Object> getCoordingByTaskId(String taskId) {
        Task task = taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        String processDefinitionId = task.getProcessDefinitionId();
        // ProcessDefinitionEntity对象对应 .bpmn文件
        ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);

        String processInstanceId = task.getProcessInstanceId();
        // 使用processInstanceId查询正在执行的执行对象表，获取流程实例对象
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();
        // 获取当前活动id
        String currentActivityId = processInstance.getActivityId();
        // 获取当前活动对象
        ActivityImpl currentActivity = processDefinitionEntity.findActivity(currentActivityId);
        Map<String, Object> map = new HashMap<>();
        map.put("x", currentActivity.getX());
        map.put("y", currentActivity.getY());
        map.put("width", currentActivity.getWidth());
        map.put("height", currentActivity.getHeight());
        return map;
    }

}
